#include "ByteCodeReader.h"
#include "Misc/Assert.h"
//#include "ScriptRuntime/Type/GSharpClass.h"

CByteCodeReader::CByteCodeReader(std::shared_ptr<CByteCodeFile> InByteCodeFile, TOptional<uint32_t> InOffset)
{
    ByteCodeFile = InByteCodeFile;

    Begin();

    if(InOffset.HasValue())
    {
        Offset = InOffset;
    }
}

CByteCodeReader &CByteCodeReader::operator>>(EByteCodeInstructions &Instruction)
{
    uint16_t InstructionValue;
    *this >> InstructionValue;
    Instruction = (EByteCodeInstructions)InstructionValue;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(EByteCodeSectionType &SectionType)
{
    uint8_t SectionTypeValue;
    *this >> SectionTypeValue;
    SectionType = (EByteCodeSectionType)SectionTypeValue;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(uint32_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= ((uint32_t)*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 8;
    Offset += 1;
    Value |= ((uint32_t)*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 16;
    Offset += 1;
    Value |= ((uint32_t)*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 24;
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(uint64_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 8;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 16;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 24;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 32;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 40;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 48;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 56;
    Offset += 1;
    return *this;

}

CByteCodeReader &CByteCodeReader::operator>>(int8_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(int16_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset) << 8;
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(float &Value)
{
    //small endian
    uint32_t ValueInt;
    *this >> ValueInt;
    Value = *(float*)&ValueInt;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(double &Value)
{
    //small endian
    uint64_t ValueInt;
    *this >> ValueInt;
    Value = *(double*)&ValueInt;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(STypeID &Value)
{
    CString TypeFullName;
    *this>>TypeFullName;

    uint8_t bIsScriptType;
    *this>>bIsScriptType;

    Value=STypeID(TypeFullName,!(bool)bIsScriptType);

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(int32_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset) << 8;
    Offset += 1;
    Value |= *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset) << 16;
    Offset += 1;
    Value |= *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset) << 24;
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(uint16_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset) << 8;
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(uint8_t &Value)
{
    Value = ByteCodeFile->ByteCode[Offset];
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CString &Value)
{
    uint32_t Index;
    *this >> Index;
    Value = StringTable[Index];
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CConstantValue &Value)
{
    uint8_t TypeValue;
    *this >> TypeValue;
    Value.Type = (EConstantValueType)TypeValue;

    switch(Value.Type)
    {
    case EConstantValueType::Char:
        *this >> Value.CharValue;
        break;
    case EConstantValueType::Bool:
        *this >> Value.BoolValue;
        break;
    case EConstantValueType::Int8:
        *this >> Value.Int8Value;
        break;
    case EConstantValueType::Int16:
        *this >> Value.Int16Value;
        break;
    case EConstantValueType::Int32:
        *this >> Value.Int32Value;
        break;
    case EConstantValueType::Int64:
        *this >> Value.Int64Value;
        break;
    case EConstantValueType::UInt8:
        *this >> Value.UInt8Value;
        break;
    case EConstantValueType::UInt16:
        *this >> Value.UInt16Value;
        break;
    case EConstantValueType::UInt32:
        *this >> Value.UInt32Value;
        break;
    case EConstantValueType::UInt64:
        *this >> Value.UInt64Value;
        break;
    case EConstantValueType::Float:
        *this >> Value.FloatValue;
        break;
    case EConstantValueType::Double:
        *this >> Value.DoubleValue;
        break;
    case EConstantValueType::String:
        *this >> Value.StringValue;
        break;
    default:
        assert(false && "invalid constant value type");
        break;
    }

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SBreakpointInfo &BreakpointInfo)
{
    *this >> BreakpointInfo.FileIndex;
    *this >> BreakpointInfo.SourceCodeLineNumber;
    *this >> BreakpointInfo.InstructionIndex;

    return *this;
}

// CByteCodeReader &CByteCodeReader::operator>>(CString &Value)
// {
//     uint32_t Length;
//     *this >> Length;
//     Value.reserve(Length+1);
//     for(uint32_t i = 0; i < Length; i++)
//     {
//         uint32_t Char;
//         *this >> Char;
//         Value.push_back(Char);
//     }
//     Value.push_back(U'\0');

//     return *this;
// }
CByteCodeReader &CByteCodeReader::operator >> (TVector<int64_t>& OutValue)
{
    uint32_t Size;
    *this >> Size;
    OutValue.Reserve(Size);
    for (uint32_t i = 0; i < Size; i++)
    {
        int64_t Value;
        *this >> Value;
        OutValue.Add(Value);
    }
    return *this;
}
CByteCodeReader &CByteCodeReader::operator>>(TVector<CString> &Value)
{
    uint32_t Size;
    *this >> Size;
    Value.Reserve(Size);
    for(uint32_t i = 0; i < Size; i++)
    {
        CString String;
        *this >> String;
        Value.Add(String);
    }

    return *this;
}

CByteCodeReader& CByteCodeReader::operator >> (SFunctionID& Value)
{
    *this >> Value.TypeID;
    *this >> Value.FuncitonIndex;

    return *this;
}

CByteCodeReader& CByteCodeReader::operator >> (TVector<SFunctionID>& Value)
{
    uint32_t Size;
    *this >> Size;
    Value.Reserve(Size);
    for (uint32_t i = 0; i < Size; i++)
    {
        SFunctionID FunctionID;
        *this >> FunctionID;
        Value.Add(FunctionID);
    }

    return *this;


}

CByteCodeReader &CByteCodeReader::operator>>(bool &Value)
{
    uint8_t ValueInt;
    *this >> ValueInt;
    Value = ValueInt;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(char32_t &Value)
{
    uint32_t ValueInt;
    *this >> ValueInt;
    Value = ValueInt;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(int64_t &Value)
{
    //small endian
    Value=  *(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset);
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 8;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 16;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 24;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 32;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 40;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 48;
    Offset += 1;
    Value |= (uint64_t)(*(uint8_t*)(ByteCodeFile->ByteCode.Data()+Offset)) << 56;
    Offset += 1;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(EScriptAccessLevel &AccessLevel)
{
    uint8_t AccessLevelValue;
    *this >> AccessLevelValue;
    AccessLevel = (EScriptAccessLevel)AccessLevelValue;

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SMemberDecorator &Decorator)
{
    uint8_t DecoratorValue;
    *this >> DecoratorValue;
    Decorator.bIsConst = DecoratorValue & 1;
    Decorator.bIsStatic = (DecoratorValue >> 1) & 1;

    return *this;

}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SClassInfo &ClassInfo)
{
    *this >> ClassInfo.AccessLevel;
    *this >> ClassInfo.Name;
    *this >> ClassInfo.NameScopeID;

    uint8_t Byte;
    *this >> Byte;
    ClassInfo.IsAbstract = Byte & 1;
    ClassInfo.bIsInnerType = (Byte >> 1) & 1;
    ClassInfo.bIsStruct = (Byte >> 2) & 1;
    ClassInfo.bIsEnum = (Byte >> 3) & 1;
    ClassInfo.bIsDelegate = (Byte >> 4) & 1;

    *this >> ClassInfo.TypeID;
    *this >> ClassInfo.BaseTypeID;
    *this >> ClassInfo.RawClassTypeID;

    {
        uint32_t PlaceholderNum;
        *this >> PlaceholderNum;
        ClassInfo.GenericPlaceholders.Resize(PlaceholderNum);

        for(uint32_t i = 0; i < PlaceholderNum; i++)
        {
            *this >> ClassInfo.GenericPlaceholders[i].TypeID;
            *this >> ClassInfo.GenericPlaceholders[i].Name;
        }
    }

    if(ClassInfo.bIsEnum)
    {
        *this >> ClassInfo.EnumNames;
        *this >> ClassInfo.EnumValues;
    }

    if(ClassInfo.bIsDelegate)
    {
        *this >> ClassInfo.DelegateFunctionType;
    }

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SGeneralizedClasseInfo &GeneralizedClasseInfo)
{
    *this >> GeneralizedClasseInfo.TypeID;
    *this >> GeneralizedClasseInfo.RawClassTypeID;
    bool HaveBaseType;
    *this >> HaveBaseType;
    if(HaveBaseType)
    {
        STypeID BaseType;
        *this >> BaseType;
        GeneralizedClasseInfo.BaseTypeID = BaseType;
    }
    *this >> GeneralizedClasseInfo.Name;
    *this >> GeneralizedClasseInfo.TemplateID;
    uint32_t TypeArgumentNum;
    *this >> TypeArgumentNum;
    GeneralizedClasseInfo.ArgumentInfos.Reserve(TypeArgumentNum);
    for(uint32_t i = 0; i < TypeArgumentNum; i++)
    {
        CByteCodeFile::STypeInfo ArgumentType;
        *this >> ArgumentType;
        GeneralizedClasseInfo.ArgumentInfos.Add(ArgumentType);
    }

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SFunctionType &FunctionType)
{
    *this >> FunctionType.TypeID;
    *this >> FunctionType.Name;
    *this >> FunctionType.ReturnType;
    *this >> FunctionType.ParameterTypes;
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SFieldInfo &FieldInfo)
{
    *this >> FieldInfo.AccessLevel;
    *this >> FieldInfo.Decorator;
    *this >> FieldInfo.Type;
    *this >> FieldInfo.Name;
    *this >> FieldInfo.FieldIndex;
    bool bHaveDefaultValue;
    *this >> bHaveDefaultValue;
    if(bHaveDefaultValue)
    {
        CConstantValue DefaultValue;
        *this >> DefaultValue;
        FieldInfo.DefaultValue = DefaultValue;
    }

    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SFunctionInfo &FunctionInfo)
{
    *this >> FunctionInfo.AccessLevel;
    *this >> FunctionInfo.bIsStatic;
    *this >> FunctionInfo.ReturnType;
    *this >> FunctionInfo.Name;

    uint32_t ParameterNum;
    *this >> ParameterNum;
    FunctionInfo.Parameters.Reserve(ParameterNum);
    for(uint32_t i = 0; i < ParameterNum; i++)
    {
        CByteCodeFile::SVariableInfo ParameterInfo;
        *this >> ParameterInfo;
        FunctionInfo.Parameters.Add(ParameterInfo);
    }


    uint32_t VariableNum;
    *this >> VariableNum;
    FunctionInfo.VariableStack.Reserve(VariableNum);
    for(uint32_t i = 0; i < VariableNum; i++)
    {
        CByteCodeFile::SVariableInfo VariableInfo;
        *this >> VariableInfo;
        FunctionInfo.VariableStack.Add(VariableInfo);
    }

    //load line positions
    *this >> FunctionInfo.LinePositions;

    uint32_t BreakablePointsNum;
    *this >> BreakablePointsNum;
    FunctionInfo.BreakablePoints.Reserve(BreakablePointsNum);
    for(uint32_t i = 0; i < BreakablePointsNum; i++)
    {
        CByteCodeFile::SBreakpointInfo Point;
        *this >> Point;
        FunctionInfo.BreakablePoints.Add(Point);
    }
    
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SFunctionCallInfo &FunctionCallInfo)
{
    *this >> FunctionCallInfo.Class;
    *this >> FunctionCallInfo.Function; 

    return *this;
}

uint32_t CByteCodeReader::GetOffset() const
{
    return Offset;
}

void CByteCodeReader::SetOffset(uint32_t InOffset)
{
    Offset = InOffset;
}

std::shared_ptr<CByteCodeFile> CByteCodeReader::GetByteCodeFile() const
{
    return ByteCodeFile;
}

SDecoratedType CByteCodeReader::ParseTypeInfo(const CByteCodeFile::STypeInfo &InInfo)
{
    SDecoratedType DecoratedType;
    DecoratedType.BasicType =CType::FindType(InInfo.TypeID);

    
    GASSERT(DecoratedType.BasicType
        ,CString(U"找不到类型： ")+InInfo.TypeID.GetTypeFullName());
    GASSERT(!DecoratedType.BasicType->IsClassType(),U"Byte code should use shared object ptr type");
    
    DecoratedType.OverrideGenericTypes.Reserve(InInfo.OverrideGenericTypes.Num());
    for(uint32_t i = 0; i < InInfo.OverrideGenericTypes.Num(); i++)
    {
        DecoratedType.OverrideGenericTypes.Add(CType::FindType(InInfo.OverrideGenericTypes[i]));
    }
    // if(Result && Result->IsClassType())
    // {
    //     CClass* Class=static_cast<CClass*>(Result);
    //     Result= Class->SharedPtrType;
    //     assert(Result);
    // }

    return DecoratedType;
}

void CByteCodeReader::Begin()
{
    //read string table
    uint32_t StringTableSize;
    *this >> StringTableSize;
    StringTable.Reserve(StringTableSize);

    for(uint32_t i = 0; i < StringTableSize; i++)
    {
        TVector<char32_t> ContentString;
        uint32_t StringLength;
        *this >> StringLength;
        ContentString.Reserve(StringLength);

        for(uint32_t j = 0; j < StringLength; j++)
        {
            uint32_t Char;
            *this >> Char;
            ContentString.Add((char32_t)Char);
        }
        ContentString.Add(U'\0');
        StringTable.Add(CString(ContentString.Data()));
    }

    //read file table
    uint32_t FileTableSize;
    *this >> FileTableSize;
    SourceCodeFileTable.Reserve(FileTableSize);

    for(uint32_t i = 0; i < FileTableSize; i++)
    {
        CString Path;
        *this >> Path;
        uint32_t LineCount;
        *this >> LineCount;

        SourceCodeFileTable.Add(CByteCodeFile::SSourceCodeFile { SPath(Path),LineCount } );
    }
}

bool CByteCodeReader::Finished()
{
    return Offset >= ByteCodeFile->ByteCode.Num();
}

CByteCodeFile::SSourceCodeFile &CByteCodeReader::GetSourceFile(uint32_t Index)
{
    return SourceCodeFileTable[Index];
}

uint32_t CByteCodeReader::GetSourceFileCount()
{
    return SourceCodeFileTable.Num();
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::STypeInfo &TypeInfo)
{
    *this >> TypeInfo.TypeID;
    uint8_t OverrideGenericTypesNum;
    *this >> OverrideGenericTypesNum;
    TypeInfo.OverrideGenericTypes.Reserve(OverrideGenericTypesNum);
    for(uint32_t i = 0; i < OverrideGenericTypesNum; i++)
    {
        STypeID OverrideType;
        *this >> OverrideType;
        TypeInfo.OverrideGenericTypes.Add(OverrideType);
    }

    return *this;
}

CByteCodeReader& CByteCodeReader::operator >> (TVector<CByteCodeFile::STypeInfo>& TypeInfos)
{
    uint32_t Size;
    *this >> Size;
    TypeInfos.Reserve(Size);
    for (uint32_t i = 0; i < Size; i++)
    {
        CByteCodeFile::STypeInfo TypeInfo;
        *this >> TypeInfo;
        TypeInfos.Add(TypeInfo);
    }
    return *this;
}

CByteCodeReader &CByteCodeReader::operator>>(CByteCodeFile::SVariableInfo &VariableInfo)
{
    *this >> VariableInfo.Type;
    *this >> VariableInfo.Name;
    return *this;
}

CByteCodeReader& CByteCodeReader::operator >> (TVector<uint32_t>& InValue)
{
    uint32_t Size;
    *this >> Size;
    InValue.Reserve(Size);
    for (uint32_t i = 0; i < Size; i++)
    {
        uint32_t Value;
        *this >> Value;
        InValue.Add(Value);
    }
    return *this;
}  